/*************************************************************************
 *                                                                       *
 * This file is part of the DogosFrame distribution.  For full copyright *
 * information, see COPYRIGHT and COPYING.LESSER.                        *
 *                                                                       *
 * Author:           sqbang                                              *
 * Email:            sqbang@ustc.edu                                     *
 *                                                                       *
 *************************************************************************/

//declaration header
#include "COMMON/pars/MemoryDatabase.h"

//COMMON headers
#include "COMMON/pars/Database.h"

//C++ headers
#include <stdlib.h>

#define MEMORY_DB_ERROR(X) \
   do {                                         \
      POUT << "MemoryDatabase: " << X << std::endl << std::flush;       \
      printClassData(POUT);                                             \
      POUT << "Program abort called..." << std::endl << std::flush;     \
      abort();                                              \
   } while (0)

#if !defined(__BGL_FAMILY__) && defined(__xlC__)
/*
 * Suppress XLC warnings
 * o */
#pragma report(disable, CPPC5334)
#pragma report(disable, CPPC5328)
#endif

namespace DOGOS
{

const int MemoryDatabase::PRINT_DEFAULT = 1;
const int MemoryDatabase::PRINT_INPUT = 2;
const int MemoryDatabase::PRINT_UNUSED = 4;
const int MemoryDatabase::SSTREAM_BUFFER = 4096;

MemoryDatabase::MemoryDatabase(
    const std::string& name):
    d_database_name(name)
{
}

/*
 *************************************************************************
 *
 * The virtual destructor deallocates database data.
 *
 *************************************************************************
 */

MemoryDatabase::~MemoryDatabase()
{
}

/*
 *************************************************************************
 *
 * Create memory data file specified by name.
 *
 *************************************************************************
 */

bool
MemoryDatabase::create(
    const std::string& name)
{
    d_database_name = name;
    d_keyvalues.clear();

    return true;
}

/*
 *************************************************************************
 *
 * Open memory data file specified by name
 *
 *************************************************************************
 */

bool
MemoryDatabase::open(
    const std::string& name,
    const bool read_write_mode)
{
    if (read_write_mode == false) {
        TBOX_ERROR("MemoryDatabase::open: MemoryDatabase only supports\n"
                   << "read-write mode.  The read_write_mode flag must be true.");

    }
    d_database_name = name;
    d_keyvalues.clear();

    return true;
}

/*
 *************************************************************************
 *
 * Close the open data file.
 *
 *************************************************************************
 */

bool
MemoryDatabase::close()
{
    d_database_name = "";
    d_keyvalues.clear();

    return true;
}

/*
 *************************************************************************
 *
 * Return whether the key exists in the database.
 *
 *************************************************************************
 */

bool
MemoryDatabase::keyExists(
    const std::string& key)
{
    return findKeyData(key) ? true : false;
}

/*
 *************************************************************************
 *
 * Return all of the keys in the database.
 *
 *************************************************************************
 */

std::vector<std::string>
MemoryDatabase::getAllKeys()
{
    std::vector<std::string> keys(d_keyvalues.size());

    std::vector<std::string>::size_type k = 0;
    for (std::list<KeyData>::iterator i = d_keyvalues.begin();
            i != d_keyvalues.end(); ++i) {
        keys[k++] = i->d_key;
    }

    return keys;
}

/*
 *************************************************************************
 *
 * Get the type of the array entry associated with the specified key
 *
 *************************************************************************
 */
DataType
MemoryDatabase::getArrayType(
    const std::string& key) {
    KeyData* keydata = findKeyData(key);

    if (keydata) {
        return keydata->d_type;
    }
    else {
        return DataType::INVALID_VALUE;
    }
}

/*
 *************************************************************************
 *
 * Get the size of the array entry associated with the specified key;
 * return 0 if the key does not exist.
 *
 *************************************************************************
 */

size_t
MemoryDatabase::getArraySize(
    const std::string& key)
{
    KeyData* keydata = findKeyData(key);
    if (keydata && keydata->d_type != DataType::DATABASE) {
        return keydata->d_array_size;
    }
    else {
        return 0;
    }
}

/*
 *************************************************************************
 *
 * Member functions that manage the database values within the database.
 *
 *************************************************************************
 */

bool
MemoryDatabase::isDatabase(
    const std::string& key)
{
    KeyData* keydata = findKeyData(key);
    return keydata ? keydata->d_type == DataType::DATABASE : false;
}



Database*
MemoryDatabase::putDatabase(
    const std::string& key)
{
    deleteKeyIfFound(key);
    KeyData keydata;
    keydata.d_key = key;
    keydata.d_type = DataType::DATABASE;
    keydata.d_array_size = 1;
    keydata.d_accessed = false;
    keydata.d_from_default = false;
    keydata.d_database = new MemoryDatabase (key);
    d_keyvalues.push_back(keydata);
    return keydata.d_database;
}



Database*
MemoryDatabase::getDatabase(
    const std::string& key)
{
    KeyData* keydata = findKeyDataOrExit(key);
    if (keydata->d_type != DataType::DATABASE) {
        MEMORY_DB_ERROR("Key=" << key << " is not a database...");
    }
    keydata->d_accessed = true;
    return keydata->d_database;
}

/*
 *************************************************************************
 *
 * Member functions that manage boolean values within the database.
 *
 *************************************************************************
 */

bool
MemoryDatabase::isBool(
    const std::string& key)
{
    KeyData* keydata = findKeyData(key);
    return keydata ? keydata->d_type == DataType::BOOL : false;
}

void
MemoryDatabase::putBool(
    const std::string& key,
    const bool& data)
{
    putBoolArray(key, &data, 1);
}

void
MemoryDatabase::putBoolArray(
    const std::string& key,
    const bool * const data,
    const size_t nelements)
{
    deleteKeyIfFound(key);
    KeyData keydata;
    keydata.d_key = key;
    keydata.d_type = DataType::BOOL;
    keydata.d_array_size = nelements;
    keydata.d_accessed = false;
    keydata.d_from_default = false;
    keydata.d_boolean = std::vector<bool>(nelements);

    for (size_t i = 0; i < nelements; ++i) {
        keydata.d_boolean[i] = data[i];
    }

    d_keyvalues.push_back(keydata);
}

bool
MemoryDatabase::getBool(
    const std::string& key)
{
    KeyData* keydata = findKeyDataOrExit(key);
    if ((keydata->d_type != DataType::BOOL) ||
            (keydata->d_array_size != 1)) {
        MEMORY_DB_ERROR("Key=" << key << " is not a boolean scalar...");
    }
    keydata->d_accessed = true;
    return keydata->d_boolean[0];
}

bool
MemoryDatabase::getBoolWithDefault(
    const std::string& key,
    const bool& defaultvalue)
{
    KeyData* keydata = findKeyData(key);
    if (keydata) return getBool(key);

    putBool(key, defaultvalue);
    d_keyvalues.back().d_from_default = true;
    return defaultvalue;
}

std::vector<bool>
MemoryDatabase::getBoolVector(
    const std::string& key)
{
    KeyData* keydata = findKeyDataOrExit(key);
    if (keydata->d_type != DataType::BOOL) {
        MEMORY_DB_ERROR("Key=" << key << " is not a boolean...");
    }
    keydata->d_accessed = true;
    return keydata->d_boolean;
}

void
MemoryDatabase::getBoolArray(
    const std::string& key,
    bool* data,
    const size_t nelements)
{
    std::vector<bool> tmp = getBoolVector(key);
    const size_t tsize = tmp.size();

    if (nelements != tsize) {
        MEMORY_DB_ERROR(
            "Incorrect array size=" << nelements << " specified for key="
            << key << " with array size="
            << tsize << "...");
    }

    for (size_t i = 0; i < tsize; ++i) {
        data[i] = tmp[i];
    }
}


/*
 *************************************************************************
 *
 * Member functions that manage character values within the database.
 *
 *************************************************************************
 */

bool
MemoryDatabase::isChar(
    const std::string& key)
{
    KeyData* keydata = findKeyData(key);
    return keydata ? keydata->d_type == DataType::CHAR : false;
}

void
MemoryDatabase::putChar(
    const std::string& key,
    const char& data)
{
    putCharArray(key, &data, 1);
}

void
MemoryDatabase::putCharVector(
    const std::string& key,
    const std::vector<char>& data)
{
    putCharArray(key, &data[0], data.size());
}

void
MemoryDatabase::putCharArray(
    const std::string& key,
    const char * const data,
    const size_t nelements)
{
    deleteKeyIfFound(key);
    KeyData keydata;
    keydata.d_key = key;
    keydata.d_type = DataType::CHAR;
    keydata.d_array_size = nelements;
    keydata.d_accessed = false;
    keydata.d_from_default = false;
    keydata.d_char = std::vector<char>(nelements);

    for (size_t i = 0; i < nelements; ++i) {
        keydata.d_char[i] = data[i];
    }

    d_keyvalues.push_back(keydata);
}

char
MemoryDatabase::getChar(
    const std::string& key)
{
    KeyData* keydata = findKeyDataOrExit(key);
    if ((keydata->d_type != DataType::CHAR) ||
            (keydata->d_array_size != 1)) {
        MEMORY_DB_ERROR("Key=" << key << " is not a single character...");
    }
    keydata->d_accessed = true;
    return keydata->d_char[0];
}

char
MemoryDatabase::getCharWithDefault(
    const std::string& key,
    const char& defaultvalue)
{
    KeyData* keydata = findKeyData(key);
    if (keydata) return getChar(key);

    putChar(key, defaultvalue);
    d_keyvalues.back().d_from_default = true;
    return defaultvalue;
}

std::vector<char>
MemoryDatabase::getCharVector(
    const std::string& key)
{
    KeyData* keydata = findKeyDataOrExit(key);
    if (keydata->d_type != DataType::CHAR) {
        MEMORY_DB_ERROR("Key=" << key << " is not a character...");
    }
    keydata->d_accessed = true;
    return keydata->d_char;
}

void
MemoryDatabase::getCharArray(
    const std::string& key,
    char* data,
    const size_t nelements)
{
    std::vector<char> tmp = getCharVector(key);
    const size_t tsize = tmp.size();

    if (nelements != tsize) {
        MEMORY_DB_ERROR(
            "Incorrect array size=" << nelements << " specified for key="
            << key << " with array size="
            << tsize << "...");
    }

    for (size_t i = 0; i < tsize; ++i) {
        data[i] = tmp[i];
    }
}

/*
 *************************************************************************
 *
 * Member functions that manage complex values within the database.
 * Note that complex numbers may be promoted from integers, floats,
 * and doubles.
 *
 *************************************************************************
 */

bool
MemoryDatabase::isComplex(
    const std::string& key)
{
    KeyData* keydata = findKeyData(key);
    return !keydata ? false : (keydata->d_type == DataType::COMPLEX
                               || keydata->d_type == DataType::INTEGER
                               || keydata->d_type == DataType::FLOAT
                               || keydata->d_type == DataType::DOUBLE);
}

void
MemoryDatabase::putComplex(
    const std::string& key,
    const Complex& data)
{
    putComplexArray(key, &data, 1);
}

void
MemoryDatabase::putComplexVector(
    const std::string& key,
    const std::vector<Complex>& data)
{
    putComplexArray(key, &data[0], data.size());
}

void
MemoryDatabase::putComplexArray(
    const std::string& key,
    const Complex * const data,
    const size_t nelements)
{
    deleteKeyIfFound(key);
    KeyData keydata;
    keydata.d_key = key;
    keydata.d_type = DataType::COMPLEX;
    keydata.d_array_size = nelements;
    keydata.d_accessed = false;
    keydata.d_from_default = false;
    keydata.d_complex = std::vector<Complex>(nelements);

    for (size_t i = 0; i < nelements; ++i) {
        keydata.d_complex[i] = data[i];
    }

    d_keyvalues.push_back(keydata);
}

Complex
MemoryDatabase::getComplex(
    const std::string& key)
{
    Complex value(0.0, 0.0);
    KeyData* keydata = findKeyDataOrExit(key);

    if (keydata->d_array_size != 1) {
        MEMORY_DB_ERROR("Key=" << key << " is not a single complex...");
    }

    switch (keydata->d_type) {
    case DataType::INTEGER:
        value = Complex((double)keydata->d_integer[0], 0.0);
        break;
    case DataType::FLOAT:
        value = Complex((double)keydata->d_float[0], 0.0);
        break;
    case DataType::DOUBLE:
        value = Complex(keydata->d_double[0], 0.0);
        break;
    case DataType::COMPLEX:
        value = keydata->d_complex[0];
        break;
    default:
        MEMORY_DB_ERROR("Key=" << key << " is not a single complex...");
    }

    keydata->d_accessed = true;
    return value;
}

Complex
MemoryDatabase::getComplexWithDefault(
    const std::string& key,
    const Complex& defaultvalue)
{
    KeyData* keydata = findKeyData(key);
    if (keydata) return getComplex(key);

    putComplex(key, defaultvalue);
    d_keyvalues.back().d_from_default = true;
    return defaultvalue;
}

std::vector<Complex>
MemoryDatabase::getComplexVector(
    const std::string& key)
{
    KeyData* keydata = findKeyDataOrExit(key);
    std::vector<Complex> array;
    switch (keydata->d_type) {
    case DataType::INTEGER: {
            array = std::vector<Complex>(keydata->d_integer.size());
            for (std::vector<Complex>::size_type i = 0;
                    i < keydata->d_integer.size(); ++i) {
                array[i] = Complex((double)keydata->d_integer[i], 0.0);
            }
            break;
        }
    case DataType::FLOAT: {
            array = std::vector<Complex>(keydata->d_float.size());
            for (std::vector<Complex>::size_type i = 0;
                    i < keydata->d_float.size(); ++i) {
                array[i] = Complex((double)keydata->d_float[i], 0.0);
            }
            break;
        }
    case DataType::DOUBLE: {
            array = std::vector<Complex>(keydata->d_double.size());
            for (std::vector<Complex>::size_type i = 0;
                    i < keydata->d_double.size(); ++i) {
                array[i] = Complex(keydata->d_double[i], 0.0);
            }
            break;
        }
    case DataType::COMPLEX:
        array = keydata->d_complex;
        break;
    default:
        MEMORY_DB_ERROR("Key=" << key << " is not a complex...");
    }
    keydata->d_accessed = true;
    return array;
}

void
MemoryDatabase::getComplexArray(
    const std::string& key,
    Complex* data,
    const size_t nelements)
{
    std::vector<Complex> tmp = getComplexVector(key);
    const size_t tsize = tmp.size();

    if (nelements != tsize) {
        MEMORY_DB_ERROR(
            "Incorrect array size=" << nelements << " specified for key="
            << key << " with array size="
            << tsize << "...");
    }

    for (size_t i = 0; i < tsize; ++i) {
        data[i] = tmp[i];
    }
}

/*
 *************************************************************************
 *
 * Member functions that manage double values within the database.
 * Note that doubles may be promoted from integers or floats.
 *
 *************************************************************************
 */

bool
MemoryDatabase::isDouble(
    const std::string& key)
{
    KeyData* keydata = findKeyData(key);
    return !keydata ? false : (keydata->d_type == DataType::DOUBLE
                               || keydata->d_type == DataType::INTEGER
                               || keydata->d_type == DataType::FLOAT);
}

void
MemoryDatabase::putDouble(
    const std::string& key,
    const double& data)
{
    putDoubleArray(key, &data, 1);
}

void
MemoryDatabase::putDoubleVector(
    const std::string& key,
    const std::vector<double>& data)
{
    putDoubleArray(key, &data[0], data.size());
}

void
MemoryDatabase::putDoubleArray(
    const std::string& key,
    const double * const data,
    const size_t nelements)
{
    deleteKeyIfFound(key);
    KeyData keydata;
    keydata.d_key = key;
    keydata.d_type = DataType::DOUBLE;
    keydata.d_array_size = nelements;
    keydata.d_accessed = false;
    keydata.d_from_default = false;
    keydata.d_double = std::vector<double>(nelements);

    for (size_t i = 0; i < nelements; ++i) {
        keydata.d_double[i] = data[i];
    }

    d_keyvalues.push_back(keydata);
}

double
MemoryDatabase::getDouble(
    const std::string& key)
{
    double value = 0.0;
    KeyData* keydata = findKeyDataOrExit(key);

    if (keydata->d_array_size != 1) {
        MEMORY_DB_ERROR("Key=" << key << " is not a single double...");
    }

    switch (keydata->d_type) {
    case DataType::INTEGER:
        value = (double)keydata->d_integer[0];
        break;
    case DataType::FLOAT:
        value = (double)keydata->d_float[0];
        break;
    case DataType::DOUBLE:
        value = keydata->d_double[0];
        break;
    default:
        MEMORY_DB_ERROR("Key=" << key << " is not a single double...");
    }

    keydata->d_accessed = true;
    return value;
}

double
MemoryDatabase::getDoubleWithDefault(
    const std::string& key,
    const double& defaultvalue)
{
    KeyData* keydata = findKeyData(key);
    if (keydata) return getDouble(key);

    putDouble(key, defaultvalue);
    d_keyvalues.back().d_from_default = true;
    return defaultvalue;
}

std::vector<double>
MemoryDatabase::getDoubleVector(
    const std::string& key)
{
    KeyData* keydata = findKeyDataOrExit(key);
    std::vector<double> array;
    switch (keydata->d_type) {
    case DataType::INTEGER: {
            array = std::vector<double>(keydata->d_integer.size());
            for (size_t i = 0; i < keydata->d_integer.size(); ++i) {
                array[i] = (double)keydata->d_integer[i];
            }
            break;
        }
    case DataType::FLOAT: {
            array = std::vector<double>(keydata->d_float.size());
            for (size_t i = 0; i < keydata->d_float.size(); ++i) {
                array[i] = (double)keydata->d_float[i];
            }
            break;
        }
    case DataType::DOUBLE: {
            array = keydata->d_double;
            break;
        }
    default:
        MEMORY_DB_ERROR("Key=" << key << " is not a double...");
    }
    keydata->d_accessed = true;
    return array;
}

void
MemoryDatabase::getDoubleArray(
    const std::string& key,
    double* data,
    const size_t nelements)
{
    std::vector<double> tmp = getDoubleVector(key);
    const size_t tsize = tmp.size();

    if (nelements != tsize) {
        MEMORY_DB_ERROR(
            "Incorrect array size=" << nelements << " specified for key="
            << key << " with array size="
            << tsize << "...");
    }

    for (size_t i = 0; i < tsize; ++i) {
        data[i] = tmp[i];
    }
}

/*
 *************************************************************************
 *
 * Member functions that manage float values within the database.
 * Note that floats may be promoted from integers or truncated from
 * doubles (without a warning).
 *
 *************************************************************************
 */

bool
MemoryDatabase::isFloat(
    const std::string& key)
{
    KeyData* keydata = findKeyData(key);
    return !keydata ? false : (keydata->d_type == DataType::DOUBLE
                               || keydata->d_type == DataType::INTEGER
                               || keydata->d_type == DataType::FLOAT);
}

void
MemoryDatabase::putFloat(
    const std::string& key,
    const float& data)
{
    putFloatArray(key, &data, 1);
}

void
MemoryDatabase::putFloatVector(
    const std::string& key,
    const std::vector<float>& data)
{
    putFloatArray(key, &data[0], data.size());
}

void
MemoryDatabase::putFloatArray(
    const std::string& key,
    const float * const data,
    const size_t nelements)
{
    deleteKeyIfFound(key);
    KeyData keydata;
    keydata.d_key = key;
    keydata.d_type = DataType::FLOAT;
    keydata.d_array_size = nelements;
    keydata.d_accessed = false;
    keydata.d_from_default = false;
    keydata.d_float = std::vector<float>(nelements);

    for (size_t i = 0; i < nelements; ++i) {
        keydata.d_float[i] = data[i];
    }

    d_keyvalues.push_back(keydata);
}

float
MemoryDatabase::getFloat(
    const std::string& key)
{

// Disable Intel warning about conversions
#ifdef __INTEL_COMPILER
#pragma warning (disable:810)
#endif

    float value = 0.0;
    KeyData* keydata = findKeyDataOrExit(key);

    if (keydata->d_array_size != 1) {
        MEMORY_DB_ERROR("Key=" << key << " is not a single float...");
    }

    switch (keydata->d_type) {
    case DataType::INTEGER:
        value = static_cast<float>(keydata->d_integer[0]);
        break;
    case DataType::FLOAT:
        value = keydata->d_float[0];
        break;
    case DataType::DOUBLE:
        value = static_cast<float>(keydata->d_double[0]);
        break;
    default:
        MEMORY_DB_ERROR("Key=" << key << " is not a single float...");
    }

    keydata->d_accessed = true;
    return value;
}

float
MemoryDatabase::getFloatWithDefault(
    const std::string& key,
    const float& defaultvalue)
{
    KeyData* keydata = findKeyData(key);
    if (keydata) return getFloat(key);

    putFloat(key, defaultvalue);
    d_keyvalues.back().d_from_default = true;
    return defaultvalue;
}

std::vector<float>
MemoryDatabase::getFloatVector(
    const std::string& key)
{
    KeyData* keydata = findKeyDataOrExit(key);
    std::vector<float> array;
    switch (keydata->d_type) {
    case DataType::INTEGER: {
            array = std::vector<float>(keydata->d_integer.size());
            for (size_t i = 0; i < keydata->d_integer.size(); ++i) {
                array[i] = static_cast<float>(keydata->d_integer[i]);
            }
            break;
        }
    case DataType::FLOAT:
        array = keydata->d_float;
        break;
    case DataType::DOUBLE: {
            array = std::vector<float>(keydata->d_double.size());
            for (size_t i = 0; i < keydata->d_double.size(); ++i) {
                array[i] = static_cast<float>(keydata->d_double[i]);
            }
            break;
        }
    default:
        MEMORY_DB_ERROR("Key=" << key << " is not a float...");
    }
    keydata->d_accessed = true;
    return array;
}

void
MemoryDatabase::getFloatArray(
    const std::string& key,
    float* data,
    const size_t nelements)
{
    std::vector<float> tmp = getFloatVector(key);
    const size_t tsize = tmp.size();

    if (nelements != tsize) {
        MEMORY_DB_ERROR(
            "Incorrect array size=" << nelements << " specified for key="
            << key << " with array size="
            << tsize << "...");
    }

    for (size_t i = 0; i < tsize; ++i) {
        data[i] = tmp[i];
    }
}

/*
 *************************************************************************
 *
 * Member functions that manage integer values within the database.
 *
 *************************************************************************
 */

bool
MemoryDatabase::isInteger(
    const std::string& key)
{
    KeyData* keydata = findKeyData(key);
    return !keydata ? false : keydata->d_type == DataType::INTEGER;
}

void
MemoryDatabase::putInteger(
    const std::string& key,
    const int& data)
{
    putIntegerArray(key, &data, 1);
}

void
MemoryDatabase::putIntegerVector(
    const std::string& key,
    const std::vector<int>& data)
{
    putIntegerArray(key, &data[0], data.size());
}

void
MemoryDatabase::putIntegerArray(
    const std::string& key,
    const int * const data,
    const size_t nelements)
{
    deleteKeyIfFound(key);
    KeyData keydata;
    keydata.d_key = key;
    keydata.d_type = DataType::INTEGER;
    keydata.d_array_size = nelements;
    keydata.d_accessed = false;
    keydata.d_from_default = false;
    keydata.d_integer = std::vector<int>(nelements);

    for (size_t i = 0; i < nelements; ++i) {
        keydata.d_integer[i] = data[i];
    }

    d_keyvalues.push_back(keydata);
}

int
MemoryDatabase::getInteger(
    const std::string& key)
{
    KeyData* keydata = findKeyDataOrExit(key);
    if ((keydata->d_type != DataType::INTEGER) ||
            (keydata->d_array_size != 1)) {
        MEMORY_DB_ERROR("Key=" << key << " is not an integer scalar...");
    }
    keydata->d_accessed = true;
    return keydata->d_integer[0];
}

int
MemoryDatabase::getIntegerWithDefault(
    const std::string& key,
    const int& defaultvalue)
{
    KeyData* keydata = findKeyData(key);
    if (keydata) return getInteger(key);

    putInteger(key, defaultvalue);
    d_keyvalues.back().d_from_default = true;
    return defaultvalue;
}

std::vector<int>
MemoryDatabase::getIntegerVector(
    const std::string& key)
{
    KeyData* keydata = findKeyDataOrExit(key);
    if (keydata->d_type != DataType::INTEGER) {
        MEMORY_DB_ERROR("Key=" << key << " is not an integer...");
    }
    keydata->d_accessed = true;
    return keydata->d_integer;
}

void
MemoryDatabase::getIntegerArray(
    const std::string& key,
    int* data,
    const size_t nelements)
{
    std::vector<int> tmp = getIntegerVector(key);
    const size_t tsize = tmp.size();

    if (nelements != tsize) {
        MEMORY_DB_ERROR(
            "Incorrect array size=" << nelements << " specified for key="
            << key << " with array size="
            << tsize << "...");
    }

    for (size_t i = 0; i < tsize; ++i) {
        data[i] = tmp[i];
    }
}

/*
 *************************************************************************
 *
 * Member functions that manage string values within the database.
 *
 *************************************************************************
 */

bool
MemoryDatabase::isString(
    const std::string& key)
{
    KeyData* keydata = findKeyData(key);
    return !keydata ? false : keydata->d_type == DataType::STRING;
}

void
MemoryDatabase::putString(
    const std::string& key,
    const std::string& data)
{
    putStringArray(key, &data, 1);
}

void
MemoryDatabase::putStringVector(
    const std::string& key,
    const std::vector<std::string>& data)
{
    putStringArray(key, &data[0], data.size());
}

void
MemoryDatabase::putStringArray(
    const std::string& key,
    const std::string * const data,
    const size_t nelements)
{
    deleteKeyIfFound(key);
    KeyData keydata;
    keydata.d_key = key;
    keydata.d_type = DataType::STRING;
    keydata.d_array_size = nelements;
    keydata.d_accessed = false;
    keydata.d_from_default = false;
    keydata.d_string = std::vector<std::string>(nelements);

    for (size_t i = 0; i < nelements; ++i) {
        keydata.d_string[i] = data[i];
    }

    d_keyvalues.push_back(keydata);
}

std::string
MemoryDatabase::getString(
    const std::string& key)
{
    KeyData* keydata = findKeyDataOrExit(key);
    if ((keydata->d_type != DataType::STRING) ||
            (keydata->d_array_size != 1)) {
        MEMORY_DB_ERROR("Key=" << key << " is not a single string...");
    }
    keydata->d_accessed = true;
    return keydata->d_string[0];
}

std::string
MemoryDatabase::getStringWithDefault(
    const std::string& key,
    const std::string& defaultvalue)
{
    KeyData* keydata = findKeyData(key);
    if (keydata) return getString(key);

    putString(key, defaultvalue);
    d_keyvalues.back().d_from_default = true;
    return defaultvalue;
}

std::vector<std::string>
MemoryDatabase::getStringVector(
    const std::string& key)
{
    KeyData* keydata = findKeyDataOrExit(key);
    if (keydata->d_type != DataType::STRING) {
        MEMORY_DB_ERROR("Key=" << key << " is not a string...");
    }
    keydata->d_accessed = true;
    return keydata->d_string;
}

void
MemoryDatabase::getStringArray(
    const std::string& key,
    std::string* data,
    const size_t nelements)
{
    std::vector<std::string> tmp = getStringVector(key);
    const size_t tsize = tmp.size();

    if (nelements != tsize) {
        MEMORY_DB_ERROR(
            "Incorrect array size=" << nelements << " specified for key="
            << key << " with array size="
            << tsize << "...");
    }

    for (size_t i = 0; i < tsize; ++i) {
        data[i] = tmp[i];
    }
}

std::string
MemoryDatabase::getName()
{
    return d_database_name;
}

std::string
MemoryDatabase::getName() const
{
    return d_database_name;
}

/*
 *************************************************************************
 *
 * Search the current database for a matching key.  If found, delete
 * that key and return true.  If the key does not exist, then return
 * false.
 *
 *************************************************************************
 */

bool MemoryDatabase::deleteKeyIfFound(
    const std::string& key)
{
    for (std::list<KeyData>::iterator i = d_keyvalues.begin();
            i != d_keyvalues.end(); ++i) {
        if (i->d_key == key) {
            d_keyvalues.erase(i);
            return true;
        }
    }
    return false;
}

/*
 *************************************************************************
 *
 * Find the key data associated with the specified key and return a
 * pointer to the record.  If no such key data exists, then return 0.
 *
 *************************************************************************
 */

MemoryDatabase::KeyData *
MemoryDatabase::findKeyData(
    const std::string& key)
{
    for (std::list<KeyData>::iterator i = d_keyvalues.begin();
            i != d_keyvalues.end(); ++i) {
        if (key == i->d_key) {
            return &(*i);
        }
    }
    return 0;
}

/*
 *************************************************************************
 *
 * Find the key data associated with the specified key and return a
 * pointer to the record.  If no such key data exists, then exit with
 * an error message.
 *
 *************************************************************************
 */

MemoryDatabase::KeyData *
MemoryDatabase::findKeyDataOrExit(
    const std::string& key)
{
    for (std::list<KeyData>::iterator i = d_keyvalues.begin();
            i != d_keyvalues.end(); ++i) {
        if (key == i->d_key) {
            return &(*i);
        }
    }
    MEMORY_DB_ERROR("Key ``" << key << "'' does not exist in the database...");
    return 0;
}

/*
 *************************************************************************
 *
 * Print the entire database to the specified output stream.
 *
 *************************************************************************
 */

void
MemoryDatabase::printClassData(
    std::ostream& os) const
{
    printDatabase(os, 0, PRINT_DEFAULT | PRINT_INPUT | PRINT_UNUSED);
}

/*
 *************************************************************************
 *
 * Print database data to the specified output stream.
 *
 *************************************************************************
 */

void
MemoryDatabase::printDatabase(
    std::ostream& os,
    const int indent,
    const int toprint) const
{
    /*
     * Get the maximum key width in the output (excluding databases)
     */

    int width = 0;
    for (std::list<KeyData>::const_iterator k = d_keyvalues.begin();
            k != d_keyvalues.end(); ++k) {
        if (((k->d_from_default) && (toprint & PRINT_DEFAULT))
                || ((k->d_accessed) && (toprint & PRINT_INPUT))
                || (!(k->d_accessed) && (toprint & PRINT_UNUSED))) {
            if (k->d_type != DataType::DATABASE) {
                const int keywidth = static_cast<int>(k->d_key.length());
                if (keywidth > width) {
                    width = keywidth;
                }
            }
        }
    }

    /*
     * Iterate over all non-database keys in the database and output key values
     */

    indentStream(os, indent);
    os << d_database_name << " {\n";
    for (std::list<KeyData>::const_iterator i = d_keyvalues.begin();
            i != d_keyvalues.end(); ++i) {

        if (((i->d_from_default) && (toprint & PRINT_DEFAULT))
                || ((i->d_accessed) && (toprint & PRINT_INPUT))
                || (!(i->d_accessed) && (toprint & PRINT_UNUSED))) {

#ifndef LACKS_SSTREAM
            std::ostringstream sstream;
#else
            char sstream_buffer[SSTREAM_BUFFER];
            std::ostrstream sstream(sstream_buffer, SSTREAM_BUFFER);
#endif

            switch (i->d_type) {

            case DataType::INVALID_VALUE: {
                    break;
                }

            case DataType::DATABASE: {
                    break;
                }

            case DataType::BOOL: {
                    indentStream(sstream, indent + 3);
                    sstream << i->d_key;
                    indentStream(sstream, width - static_cast<int>(i->d_key.length()));
                    sstream << " = ";
                    const std::vector<bool>::size_type n = i->d_boolean.size();
                    for (std::vector<bool>::size_type j = 0; j < n; ++j) {
                        sstream << (i->d_boolean[j] ? "TRUE" : "FALSE");
                        if (j < n - 1) {
                            sstream << ", ";
                        }
                    }
                    break;
                }

            case DataType::CHAR: {
                    indentStream(sstream, indent + 3);
                    sstream << i->d_key;
                    indentStream(sstream, width - static_cast<int>(i->d_key.length()));
                    sstream << " = ";
                    const std::vector<char>::size_type n = i->d_char.size();
                    for (std::vector<char>::size_type j = 0; j < n; ++j) {
                        sstream << "'" << i->d_char[j] << "'";
                        if (j < n - 1) {
                            sstream << ", ";
                        }
                    }
                    break;
                }

            case DataType::COMPLEX: {
                    indentStream(sstream, indent + 3);
                    sstream << i->d_key;
                    indentStream(sstream, width - static_cast<int>(i->d_key.length()));
                    sstream << " = ";
                    const std::vector<Complex>::size_type n = i->d_complex.size();
                    for (std::vector<Complex>::size_type j = 0; j < n; ++j) {
                        sstream << i->d_complex[j];
                        if (j < n - 1) {
                            sstream << ", ";
                        }
                    }
                    break;
                }

            case DataType::DOUBLE: {
                    indentStream(sstream, indent + 3);
                    sstream << i->d_key;
                    indentStream(sstream, width - static_cast<int>(i->d_key.length()));
                    sstream << " = ";
                    const std::vector<double>::size_type n = i->d_double.size();
                    for (std::vector<double>::size_type j = 0; j < n; ++j) {
                        sstream << i->d_double[j];
                        if (j < n - 1) {
                            sstream << ", ";
                        }
                    }
                    break;
                }

            case DataType::FLOAT: {
                    indentStream(sstream, indent + 3);
                    sstream << i->d_key;
                    indentStream(sstream, width - static_cast<int>(i->d_key.length()));
                    sstream << " = ";
                    const std::vector<float>::size_type n = i->d_float.size();
                    for (std::vector<float>::size_type j = 0; j < n; ++j) {
                        sstream << i->d_float[j];
                        if (j < n - 1) {
                            sstream << ", ";
                        }
                    }
                    break;
                }

            case DataType::INTEGER: {
                    indentStream(sstream, indent + 3);
                    sstream << i->d_key;
                    indentStream(sstream, width - static_cast<int>(i->d_key.length()));
                    sstream << " = ";
                    const std::vector<int>::size_type n = i->d_integer.size();
                    for (std::vector<int>::size_type j = 0; j < n; ++j) {
                        sstream << i->d_integer[j];
                        if (j < n - 1) {
                            sstream << ", ";
                        }
                    }
                    break;
                }

            case DataType::STRING: {
                    indentStream(sstream, indent + 3);
                    sstream << i->d_key;
                    indentStream(sstream, width - static_cast<int>(i->d_key.length()));
                    sstream << " = ";
                    const std::vector<std::string>::size_type n = i->d_string.size();
                    for (std::vector<std::string>::size_type j = 0; j < n; ++j) {
                        sstream << "\"" << i->d_string[j] << "\"";
                        if (j < n - 1) {
                            sstream << ", ";
                        }
                    }
                    break;
                }

            default:
                {
                    TBOX_FIND_BUG();
                }
            }

            /*
             * Output whether the key was used or default in column 60
             */

            if (i->d_type != DataType::DATABASE) {
#ifndef LACKS_SSTREAM
                const int tab = static_cast<int>(59 - sstream.str().length());
#else
                const int tab = static_cast<int>(59 - sstream.pcount());
#endif
                if (tab > 0) {
                    indentStream(sstream, tab);
                }
                if (i->d_from_default) {
                    sstream << " // from default";
                }
                else if (i->d_accessed) {
                    sstream << " // input used";
                }
                else {
                    sstream << " // input not used";
                }

//            sstream << std::endl << ends;
                sstream << std::endl;
                os << sstream.str();
            }
        }
    }

    /*
     * Finally, output all databases in the current key list
     */
    for (std::list<KeyData>::const_iterator j = d_keyvalues.begin();
            j != d_keyvalues.end(); ++j)
    {
        if (j->d_type == DataType::DATABASE)
        {
            MemoryDatabase* db = dynamic_cast<MemoryDatabase*>(j->d_database);
            db->printDatabase(os, indent + 3, toprint);
        }
    }

    indentStream(os, indent);
    os << "}\n";
}

}

#if !defined(__BGL_FAMILY__) && defined(__xlC__)
/*
 * Unsuppress XLC warnings
 */
#pragma report(enable, CPPC5334)
#pragma report(enable, CPPC5328)
#endif
